<template>
  <form
      class="form-validation"
      :class="{ 'form-inline': inline }"
      @submit.prevent="handleSubmit"
  >
    <div
        v-for="field in fields"
        :key="field.name"
        class="form-item"
    >
      <label
          :for="field.name"
          class="form-label"
          :style="{ width: labelWidth }"
      >{{ field.label }}</label>

      <!-- 文本输入 -->
      <input
          v-if="field.type === 'text' || field.type === 'password'"
          :id="field.name"
          v-model="formData[field.name]"
          :type="field.type"
          :disabled="disabled"
          class="form-input"
          @input="handleInput(field.name)"
          @change="handleChange(field.name)"
      >

      <!-- 下拉选择 -->
      <select
          v-else-if="field.type === 'select'"
          :id="field.name"
          v-model="formData[field.name]"
          :disabled="disabled"
          class="form-select"
          @change="handleChange(field.name)"
      >
        <option
            v-for="opt in field.options"
            :key="opt.value"
            :value="opt.value"
        >{{ opt.label }}</option>
      </select>

      <!-- 错误提示 -->
      <div
          v-if="errors[field.name]"
          class="error-message"
          :aria-live="'polite'"
      >{{ errors[field.name] }}</div>
    </div>

    <slot name="submit">
      <button
          type="submit"
          class="submit-btn"
          :disabled="disabled"
      >提交</button>
    </slot>
  </form>
</template>

<script setup>
import { ref, reactive, watch, toRaw } from 'vue'

// 内置验证规则
const builtInRules = {
  required: value => ({
    valid: !!value,
    message: '必填项'
  }),
  email: value => ({
    valid: /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
    message: '邮箱格式不正确'
  }),
  minLength: min => value => ({
    valid: value.length >= min,
    message: `至少需要${min}个字符`
  })
}

const props = defineProps({
  modelValue: {
    type: Object,
    required: true
  },
  fields: {
    type: Array,
    default: () => []
  },
  rules: {
    type: Object,
    default: () => ({})
  },
  validateOnChange: {
    type: Boolean,
    default: true
  },
  labelWidth: {
    type: [String, Number],
    default: '100px'
  },
  inline: Boolean,
  disabled: Boolean
})

const emit = defineEmits([
  'update:modelValue',
  'validate:success',
  'validate:fail',
  'submit',
  'change'
])

// 响应式数据
const formData = reactive({ ...props.modelValue })
const errors = reactive({})

// 同步外部modelValue变化
watch(() => props.modelValue, (newVal) => {
  Object.assign(formData, newVal)
})

// 输入处理
const handleInput = (fieldName) => {
  emit('update:modelValue', { ...formData })
  if (props.validateOnChange) {
    validateField(fieldName)
  }
}

// 字段变化事件
const handleChange = (fieldName) => {
  const value = formData[fieldName]
  emit('change', fieldName, value)
}

// 单个字段验证
const validateField = async (fieldName) => {
  const rules = props.rules[fieldName] || []
  const value = formData[fieldName]

  for (const rule of rules) {
    let validator = rule
    if (typeof rule === 'string' && builtInRules[rule]) {
      validator = builtInRules[rule]
    }

    if (typeof validator === 'function') {
      const result = validator(value)
      if (!result.valid) {
        errors[fieldName] = result.message
        return false
      }
    } else if (typeof validator === 'object' && validator.validator) {
      const customResult = validator.validator(value)
      if (!customResult) {
        errors[fieldName] = validator.message || '验证失败'
        return false
      }
    }
  }

  delete errors[fieldName]
  return true
}

// 整体验证
const validate = async () => {
  let isValid = true
  const errorList = []

  for (const field of props.fields) {
    const valid = await validateField(field.name)
    if (!valid) {
      isValid = false
      errorList.push({
        field: field.name,
        message: errors[field.name]
      })
    }
  }

  if (isValid) {
    emit('validate:success', toRaw(formData))
  } else {
    emit('validate:fail', errorList)
  }

  return isValid
}

// 提交处理
const handleSubmit = async () => {
  const isValid = await validate()
  if (isValid) {
    emit('submit', toRaw(formData))
  }
}

// 暴露验证方法
defineExpose({ validate })
</script>

<style scoped>
.form-validation {
  max-width: 600px;
  margin: 0 auto;
}

.form-item {
  margin-bottom: 1rem;
}

.form-label {
  display: inline-block;
  margin-right: 1rem;
  text-align: right;
}

.form-input,
.form-select {
  padding: 0.5rem;
  border: 1px solid #ccc;
  border-radius: 4px;
  width: 200px;
}

.form-inline .form-item {
  display: inline-flex;
  margin-right: 1rem;
}

.error-message {
  color: #ff4444;
  font-size: 0.875rem;
  margin-top: 0.25rem;
}

.submit-btn {
  background-color: #42b983;
  color: white;
  padding: 0.5rem 1rem;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.submit-btn:disabled {
  background-color: #ccc;
  cursor: not-allowed;
}
</style>